/**
 * Created by liujiangyong on 2025/01/14.
 */
'use strict';

const Facebody = require('@alicloud/facebody20191230');
const TeaUtil = require('@alicloud/tea-util');
const path = require('path');
const fs = require('fs');
const http = require('http');
const https = require('https');
const { PassThrough } = require('stream');




module.exports = function (app, opts) {
  const getResponse = function (httpClient, url) {
  return new Promise((resolve, reject) => {
    httpClient.get(url, function (response) {
      resolve(response);
    })
  })
}
  /**
 * 添加人脸数据
 * @param {*} ctx 
 * @param {String} imageUrl 人脸图片地址，可以是http/https，也可以是本地相对地址
 * @param {String} workerId 唯一标识：当前人脸图片对应的 b_worker.id
 * @param {String} workerName 当前人脸图片对应的人员名字，添加在 ExtraData 中
 * @returns {Object} addFace response
 */
 const addFace =async function(ctx, imageUrl, workerId, workerName) {
  const t1 = new Date().getTime();
  const { facebodyClient } = ctx.app.fs;
  const { aliCloud: { faceDbName }, uploadRootPath } = ctx.app.fs.opts;
  if (!faceDbName) { return {} };
  return new Promise(async (resolve, reject) => {
    try {
      let runtime = new TeaUtil.RuntimeOptions({
        autoretry: true,
        connectTimeout: 30000,
        readTimeout: 30000,
      });
      // 删除原人脸样本
      let deleteFaceEntityRequest = new Facebody.DeleteFaceEntityRequest({
        dbName: faceDbName,
        entityId: workerId
      });
      await facebodyClient.deleteFaceEntityWithOptions(deleteFaceEntityRequest, runtime);

      // 添加人脸样本
      let addFaceEntityRequest = new Facebody.AddFaceEntityRequest({
        dbName: faceDbName,
        entityId: workerId,
      });
      await facebodyClient.addFaceEntityWithOptions(addFaceEntityRequest, runtime);

      // 添加人脸数据
      const imageUrlObject = await urlToStream(imageUrl, uploadRootPath);
      let addFaceAdvanceRequest = new Facebody.AddFaceAdvanceRequest();
      addFaceAdvanceRequest.dbName = faceDbName;
      addFaceAdvanceRequest.entityId = workerId;
      addFaceAdvanceRequest.imageUrlObject = imageUrlObject;
      addFaceAdvanceRequest.extraData = workerName;
      // 类间相似度阈值。将新增人脸图片与其他所有 EntityId 内已有人脸进行相似比对，大于等于该阈值则表示其他 EntityId 内存在相似人脸，出现重复，添加失败。
      addFaceAdvanceRequest.similarityScoreThresholdBetweenEntity = 75;
      const res = await facebodyClient.addFaceAdvance(addFaceAdvanceRequest, runtime);

      resolve(res);
    } catch (error) {
      console.error('[addFace]发生错误', error);
      if (error.code === "InvalidImage.NotFoundFace") {
        resolve({ errMsg: "照片未识别到人脸，请重试" });
        return;
      } else if (error.code === "SimilarityScoreNotMatched") {
        resolve({ errMsg: "人脸信息重复，请确认此人员是否已添加" });
        return;
      }
      reject(error);
    } finally {
      console.log(`[addFace]耗时：${new Date().getTime() - t1} ms`);
    }
  });
}
 /**
 * 删除人脸信息
 * @param {*} ctx 
 * @param {Array} entityIds 唯一标识：当前人脸图片对应的 b_worker.id
 */
const delFace= async function(ctx, entityIds) {
  const { facebodyClient } = ctx.app.fs;
  const { aliCloud: { faceDbName } } = ctx.app.fs.opts;
  if (!Array.isArray(entityIds) || entityIds.length === 0 || !faceDbName) {
    return;
  }
  try {
    for (let entityId of entityIds) {
      let deleteFaceEntityRequest = new Facebody.DeleteFaceEntityRequest({
        dbName: faceDbName,
        entityId: entityId
      });
      let runtime = new TeaUtil.RuntimeOptions({
        autoretry: true,
        connectTimeout: 30000,
        readTimeout: 30000,
      });
      console.log(`[delFace] entityId: ${entityId}`);
      await facebodyClient.deleteFaceEntityWithOptions(deleteFaceEntityRequest, runtime);
      await delay(500); // 等待 500ms 防止 Request was denied due to user flow control 错误
    }
  } catch (error) {
    console.error(error);
  }
}
/**
 * 人脸搜索1:N
 * @param {*} ctx 
 * @param {Buffer | Base64String} faceImg 人脸图片
 * @returns {Object} searchFace response
 */
const searchFace =async(ctx, faceImg)=>{
  const { facebodyClient } = ctx.app.fs;
  const { aliCloud: { faceDbName } } = ctx.app.fs.opts;
  if (!faceDbName) { return };
  return new Promise(async (resolve, reject) => {
    try {
      const buffer = Buffer.isBuffer(faceImg) ? faceImg : Buffer.from(faceImg, 'base64');
      const readableStream = new PassThrough();
      readableStream.end(buffer);
      let requestBody = new Facebody.SearchFaceAdvanceRequest({
        dbName: faceDbName,
        imageUrlObject: readableStream,
        limit: 100,
        maxFaceNum: 10
      });
      let runtime = new TeaUtil.RuntimeOptions({
        autoretry: true,
        connectTimeout: 30000,
        readTimeout: 300000, // 5分钟
      });
      const res = await facebodyClient.searchFaceAdvance(requestBody, runtime);
      resolve(res);
    } catch (error) {
      if (error.code === "InvalidImage.NotFoundFace") {
        resolve({
          body: {
            data: {
              matchList: []
            }
          },
          message: "InvalidImage.NotFoundFace"
        });
        return;
      }
      console.error('[searchFace]发生错误', error);
      reject(error);
    }
  });
}
/**
 * 人脸对比1:1
 * @param {*} ctx 
 * @param {String} imageUrlA 人脸图片A地址，可以是http/https，也可以是本地相对地址
 * @param {String} imageUrlB 人脸图片B地址，可以是http/https，也可以是本地相对地址
 * @returns {Object} compareFace response
 */
const compareFace =async function (ctx, imageUrlA, imageUrlB) {
  const { facebodyClient } = ctx.app.fs;
  const { uploadRootPath } = ctx.app.fs.opts;
  return new Promise(async (resolve, reject) => {
    try {
      const imageUrlAObject = await urlToStream(imageUrlA, uploadRootPath);
      const imageUrlBObject = await urlToStream(imageUrlB, uploadRootPath);
      let compareFaceRequest = new Facebody.CompareFaceAdvanceRequest({
        imageURLAObject: imageUrlAObject,
        imageURLBObject: imageUrlBObject,
      });
      let runtime = new TeaUtil.RuntimeOptions({
        autoretry: true,
        connectTimeout: 30000,
        readTimeout: 30000,
      });
      const res = await facebodyClient.compareFaceAdvance(compareFaceRequest, runtime);
      resolve(res);
    } catch (error) {
      console.error('[compareFace]发生错误', error);
      if (error.code === "InvalidImage.NotFoundFace") {
        resolve({ errMsg: "照片未识别到人脸，请重试" });
        return;
      }
      reject(error);
    }
  });
}

/**
 * @param {String} imageUrl 任意可访问的url或本地static文件地址（示例：upload/image/xxx.png）
 * @param {String} rootPath 文件根目录
 * @returns {Readable Stream} fileStream
 */
async function urlToStream(imageUrl, rootPath = 'static') {
  let fileStream = null;
  if (imageUrl.startsWith('http')) {
    const url = new URL(imageUrl);
    const httpClient = (url.protocol === "https:") ? https : http;
    fileStream = await getResponse(httpClient, url);
  } else {
    const filePath = path.posix.join(__dirname, `../${rootPath}`, imageUrl)
    fileStream = fs.createReadStream(filePath);
  }
  return fileStream;
}
const delay=function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

 return  {
  addFace,
  delFace,
  searchFace,
  compareFace,
  delay,
};
}




